package kuda.tools;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * Copyright (c) 2010-2011,
 * Tayfun Elmas    <elmas@cs.berkeley.edu>
 * All rights reserved.
 * <p/>
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * <p/>
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * <p/>
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * <p/>
 * 3. The names of the contributors may not be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 * <p/>
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

public class ReadChordOutput2 {

	static HashMap<String, HashSet<String>> classFields = new HashMap<String, HashSet<String>>();

	static HashMap<String, HashSet<String>> classMethods = new HashMap<String, HashSet<String>>();

	public static class Fld {
		int id;

		String className;

		String fieldName;

		public Fld(int i, String cn, String fn) {
			this.id = i;
			this.className = cn;
			this.fieldName = fn;
		}
	}

	public static class E {
		int id;

		int M;

		int line;

		public E(int i, int m, int l) {
			this.id = i;
			this.M = m;
			this.line = l;
		}
	}

	public static class Mth {
		int id;

		String className;

		String methodName;

		public Mth(int i, String cn, String mn) {
			this.id = i;
			this.className = cn;
			this.methodName = mn;
		}
	}

	static ArrayList<Fld> fieldList;

	static ArrayList<E> EList;

	static ArrayList<Mth> methodList;

	public static void main(String[] args) {
		File inputDir = Helper.chooseDirectory();
		File outputFile = Helper.chooseFile();

		System.out.printf(
				"Starting parsing.\nInput file: %s, Output file %s\n", inputDir
						.getName(), outputFile.getName());

		parse(inputDir, outputFile);

		System.out.printf("Finished parsing instrumentation.");
		System.exit(0);

	}

	static int numE = 0, numM = 0, numC = 0, numI = 0, numL = 0;

	static File fileDataRaces = null;

	static int[][] unlockedRacePairs;

	private static Document parseXML(String fileName, File inputDir) {
		Document document = null;

		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();

			factory.setValidating(false);
			factory.setNamespaceAware(false);

			DocumentBuilder builder = factory.newDocumentBuilder();
			document = builder.parse(new File(inputDir, fileName));

			return document;

		} catch (SAXParseException spe) {
			// Error generated by the parser
			System.out.println("\n** Parsing error" + ", line "
					+ spe.getLineNumber() + ", uri " + spe.getSystemId());
			System.out.println("   " + spe.getMessage());

			// Use the contained exception, if any
			Exception x = spe;
			if (spe.getException() != null)
				x = spe.getException();
			x.printStackTrace();

		} catch (SAXException sxe) {
			// Error generated during parsing
			Exception x = sxe;
			if (sxe.getException() != null)
				x = sxe.getException();
			x.printStackTrace();

		} catch (ParserConfigurationException pce) {
			// Parser with specified options can't be built
			pce.printStackTrace();

		} catch (IOException ioe) {
			// I/O error
			ioe.printStackTrace();
		}
		return null;
	}

	private static void parseCounters(File inputDir) {
		numC = readCounter("C", inputDir);
		numE = readCounter("E", inputDir);
		numI = readCounter("I", inputDir);
		numL = readCounter("L", inputDir);
		numM = readCounter("M", inputDir);

		System.out.printf("Read counters\n");
	}

	private static int readCounter(String what, File inputDir) {
		BufferedReader reader = null;
		int num = 0;
		try {
			reader = new BufferedReader(new FileReader(new File(inputDir, "num"
					+ what + ".txt")));

			num = Integer.parseInt(reader.readLine());

			reader.close();

			return num;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}

	private static void parseDataRaces(File inputDir) {
		Document doc = parseXML("dataraces.xml", inputDir);

		Element de = doc.getDocumentElement();
		NodeList nl = de.getElementsByTagName("datarace");
		for (int i = 0; i < nl.getLength(); ++i) {
			Node node = nl.item(i);
			assert (node.getNodeName().equals("datarace"));
			int F = Integer.parseInt(node.getAttributes().getNamedItem("F")
					.getNodeValue());
			int O = Integer.parseInt(node.getAttributes().getNamedItem("O")
					.getNodeValue());

			Node access1 = node.getChildNodes().item(0);
			int T1 = Integer.parseInt(access1.getAttributes().getNamedItem("T")
					.getNodeValue());
			int C1 = Integer.parseInt(access1.getAttributes().getNamedItem("C")
					.getNodeValue());
			int E1 = Integer.parseInt(access1.getAttributes().getNamedItem("E")
					.getNodeValue());

			Node access2 = node.getChildNodes().item(1);
			int T2 = Integer.parseInt(access2.getAttributes().getNamedItem("T")
					.getNodeValue());
			int C2 = Integer.parseInt(access2.getAttributes().getNamedItem("C")
					.getNodeValue());
			int E2 = Integer.parseInt(access2.getAttributes().getNamedItem("E")
					.getNodeValue());

			System.out
					.printf(
							"Parsed a datarace with F=%d and O=%d, access1(T=%d, C=%d, E=%d), access2(T=%d, C=%d, E=%d)\n",
							F, O, T1, C1, E1, T2, C2, E2);

			String className = fieldList.get(F).className;
			String fieldName = fieldList.get(F).fieldName;

			System.out.printf("Adding field: %s.%s\n", className, fieldName);

			if (!classFields.containsKey(className)) {
				classFields.put(className, new HashSet<String>());
			}
			HashSet<String> fields = classFields.get(className);
			fields.add(fieldName);

			E e = EList.get(E1);
			Mth m = methodList.get(e.M);

			System.out.printf("Adding method: %s.%s\n", m.className,
					m.methodName);

			if (!classMethods.containsKey(m.className)) {
				classMethods.put(m.className, new HashSet<String>());
			}
			HashSet<String> methods = classMethods.get(m.className);
			methods.add(m.methodName);

			e = EList.get(E2);
			m = methodList.get(e.M);

			System.out.printf("Adding method: %s.%s\n", m.className,
					m.methodName);

			if (!classMethods.containsKey(m.className)) {
				classMethods.put(m.className, new HashSet<String>());
			}
			methods = classMethods.get(m.className);
			methods.add(m.methodName);
		}
	}

	private static void parseFields(File inputDir) {
		Document doc = parseXML("Flist.xml", inputDir);

		fieldList = new ArrayList<Fld>();

		Element de = doc.getDocumentElement();
		NodeList nl = de.getElementsByTagName("F");
		for (int i = 0; i < nl.getLength(); ++i) {
			Node node = nl.item(i);
			assert (node.getNodeName().equals("F"));
			int id = Integer.parseInt(node.getAttributes().getNamedItem("id")
					.getNodeValue());
			String name = node.getAttributes().getNamedItem("name")
					.getNodeValue().trim();
			String className = null;
			String fieldName = null;
			String parts[] = name.substring(1, name.length() - 1).trim().split(
					"[:]");
			if (parts.length < 2) {
				className = name;
				fieldName = name;
			} else {
				className = parts[0].trim();
				fieldName = parts[1].trim().split("[ ]")[1].trim();
			}
			fieldList.add(id, new Fld(i, className, fieldName));

			System.out.printf("Parsed Field classname:%s, fieldname:%s\n",
					className, fieldName);
		}
	}

	private static void parseMethods(File inputDir) {
		Document doc = parseXML("Mlist.xml", inputDir);

		methodList = new ArrayList<Mth>();

		Element de = doc.getDocumentElement();
		NodeList nl = de.getElementsByTagName("M");
		for (int i = 0; i < nl.getLength(); ++i) {
			Node node = nl.item(i);
			assert (node.getNodeName().equals("M"));
			int id = Integer.parseInt(node.getAttributes().getNamedItem("id")
					.getNodeValue());
			String name = node.getAttributes().getNamedItem("sign")
					.getNodeValue().trim();
			String className = null;
			String methodName = null;
			String parts[] = name.substring(1, name.length() - 1).trim().split(
					"[:]");
			className = parts[0].trim();
			methodName = parts[1].trim();
			methodName = methodName.split("[ ]")[1].trim();
			methodName = methodName.substring(0, methodName.indexOf("("));

			methodList.add(id, new Mth(id, className, methodName));
			System.out.printf("Parsed Method classname:%s, methodName:%s\n",
					className, methodName);
		}
	}

	private static void parseElist(File inputDir) {
		Document doc = parseXML("Elist.xml", inputDir);

		EList = new ArrayList<E>();

		Element de = doc.getDocumentElement();
		NodeList nl = de.getElementsByTagName("E");
		for (int i = 0; i < nl.getLength(); ++i) {
			Node node = nl.item(i);
			assert (node.getNodeName().equals("E"));
			int id = Integer.parseInt(node.getAttributes().getNamedItem("id")
					.getNodeValue());
			int meth = Integer.parseInt(node.getAttributes().getNamedItem(
					"meth").getNodeValue());
			int line = Integer.parseInt(node.getAttributes().getNamedItem(
					"line").getNodeValue());

			EList.add(id, new E(id, meth, line));
			System.out.printf("Parsed E mth:%d, line:%d\n", meth, line);
		}
	}

	private static void saveOutput(File outputFile) {
		PrintWriter writer = null;

		try {
			writer = new PrintWriter(outputFile);

			for (String className : classFields.keySet()) {
				HashSet<String> fields = classFields.get(className);
				writer.printf("c %s\n", className);
				for (String fieldName : fields) {
					writer.printf("f %s\n", fieldName);
				}
			}

			for (String className : classMethods.keySet()) {
				HashSet<String> methods = classMethods.get(className);
				writer.printf("c %s\n", className);
				for (String methodName : methods) {
					writer.printf("m %s\n", methodName);
				}
			}

			writer.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static void parse(File inputDir, File outputFile) {
		try {
			parseCounters(inputDir);
			parseMethods(inputDir);
			parseFields(inputDir);
			parseElist(inputDir);
			parseDataRaces(inputDir);
			saveOutput(outputFile);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
